package org.apache.ocean.main.search;

import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.concurrent.ExecutorService;

import org.apache.lucene.document.Document;
import org.apache.lucene.document.FieldSelector;
import org.apache.lucene.document.FieldSelectorResult;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.Weight;
import org.apache.ocean.main.IndexDocuments;
import org.apache.ocean.main.IndexSchema;
import org.apache.ocean.main.Snapshot;
import org.apache.ocean.main.IndexDocuments.IndexDocument;
import org.apache.ocean.main.fields.FieldType;
import org.apache.ocean.main.util.Timeout;

public class DocumentSearch extends Search {
	private Snapshot snapshot;
	private ExecutorService executorService;
	private DocumentQuery documentQuery;

	public DocumentSearch(DocumentQuery documentQuery, Snapshot snapshot, ExecutorService executorService) {
		super(documentQuery);
		this.documentQuery = documentQuery;
		this.snapshot = snapshot;
		this.executorService = executorService;
	}

	public DocumentResult call() throws Exception {
		long startTime = Timeout.TIMER_THREAD.getTime();
		IndexSchema.Category indexSchemaCategory = snapshot.getIndexSchemaCategory();
		Aggregate aggregate = documentQuery.getAggregate();
		if (aggregate != null && aggregate.isMinMax()) {
			AggregateSearch aggregateSearch = new AggregateSearch(aggregate, snapshot);
			Object value = aggregateSearch.call();
			IndexDocuments indexDocuments = new IndexDocuments();
			IndexDocument indexDocument = new IndexDocument(indexSchemaCategory);
			indexDocument.add(aggregate.getField(), value);
			indexDocuments.add(indexDocument);
			long elapsed = Timeout.TIMER_THREAD.getTime() - startTime;
			return new DocumentResult(0, indexDocuments, null, 1, elapsed);
		} else {
			SnapshotSearch snapshotSearch = new SnapshotSearch(searchQuery, snapshot, executorService);
			Result<ScoreDoc> result = snapshotSearch.call();
			List<ScoreDoc> scoreDocs = result.getDocs();
			int start = documentQuery.getStart();
			int numFound = result.getNumFound();
			IndexDocuments indexDocuments = new IndexDocuments();
			FieldSelector fieldSelector = documentQuery.getFieldSelector();
			for (ScoreDoc scoreDoc : scoreDocs) {
				IndexReader indexReader = scoreDoc.indexSnapshot.getIndexReader();
				Document document = indexReader.document(scoreDoc.doc, fieldSelector);
				IndexDocument indexDocument = new IndexDocument(document, indexSchemaCategory);
				indexDocuments.add(indexDocument);
			}
			long elapsed = Timeout.TIMER_THREAD.getTime() - startTime;
			return new DocumentResult(start, indexDocuments, scoreDocs.toArray(new ScoreDoc[0]), numFound, elapsed);
		}
	}

	public static class DocumentQuery extends SearchQuery {
		private SortedSet<String> fields;

		public DocumentQuery(Weight weight, int start, int rows, Filters filters, SortedSet<String> fields, Sort sort, Aggregate aggregate, Timeout timeout) {
			super(weight, start, rows, filters, sort, aggregate, timeout);
			this.fields = fields;
		}

		public FieldSelector getFieldSelector() {
			return new DocumentFieldSelector(fields);
		}

		public SortedSet<String> getFields() {
			return fields;
		}
	}

	public static class DocumentFieldSelector implements FieldSelector {
		private Set<String> fields;

		public DocumentFieldSelector() {
		}

		public DocumentFieldSelector(Set<String> fields) {
			this.fields = fields;
		}

		public FieldSelectorResult accept(String fieldName) {
			if (fields == null) {
				if (!fieldName.endsWith("fieldcache")) {
					return FieldSelectorResult.LOAD;
				} else {
					return FieldSelectorResult.NO_LOAD;
				}
			} else {
				if (fields.contains(fieldName)) {
					return FieldSelectorResult.LOAD;
				} else {
					return FieldSelectorResult.NO_LOAD;
				}
			}
		}
	}

	public static class DocumentResult extends Result {
		private IndexDocuments indexDocuments;

		public DocumentResult(int start, IndexDocuments indexDocuments, ScoreDoc[] scoreDocs, int numFound, long elapsed) {
			super(start, scoreDocs, numFound, elapsed);
			this.indexDocuments = indexDocuments;
		}

		public IndexDocuments getIndexDocuments() {
			return indexDocuments;
		}
	}
}
